#include <QtTest>

#include "test_SequenceModel.h"
#include "SequenceModel.h"
#include "sequenceitem.h"
#include "modelViewRules.h"

test_SequenceModel::test_SequenceModel()
{
}

/** executed before every test case */
void test_SequenceModel::init()
{
   m_itemList.clear();

   m_model = new SequenceModel( m_itemList, this);
}

/** executed after every test case */
void test_SequenceModel::cleanup()
{
   m_itemList.clear();
}

void test_SequenceModel::initTestCase()
{
}

void test_SequenceModel::cleanupTestCase()
{
}

void test_SequenceModel::testInstance()
{
   QVERIFY( m_model != NULL);

   QCOMPARE( m_model->rowCount(), 0);
   QVERIFY( m_model->headerData( 0, Qt::Vertical, Qt::DisplayRole) == QVariant());
   QCOMPARE( m_model->flags(QModelIndex()),
             Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable);
}

void test_SequenceModel::testGetData()
{
   const SequenceItem *readItem;

   SequenceItem *mediaItem = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   m_model->addSequenceItem( mediaItem);

   /* valid index */
   QCOMPARE(m_model->index(0).data( Qt::DisplayRole).toString(), QString("track_01.mp3"));

   readItem = m_model->index(0).data(modelViewRules::Binary).value<const SequenceItem *>();
   QCOMPARE( *readItem, *mediaItem);

   readItem = m_model->index(0).data( Qt::EditRole).value<const SequenceItem *>();
   QCOMPARE( *readItem, *mediaItem);
}

/* Access to invalid index without 'index' function.
 * Compare this test with previous */
void test_SequenceModel::testGetDataDirect()
{
   bool exceptionThrown = false;

   SequenceItem *mediaItem = new SequenceItem();
   m_model->addSequenceItem( mediaItem);

   try
   {
      m_model->data( m_model->index(1), Qt::EditRole);
   }
   catch( QString & msg)
   {
      exceptionThrown = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   QVERIFY( exceptionThrown == true);
}

void test_SequenceModel::testGetDataInvalid()
{
   const SequenceItem *readItem;

   SequenceItem *mediaItem = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   m_model->addSequenceItem( mediaItem);

   /* invalid index */
   QCOMPARE(m_model->index(1).data( Qt::DisplayRole).toString(), QString());

   readItem = m_model->index(1).data( Qt::EditRole).value<const SequenceItem *>();
   QCOMPARE( readItem, (const SequenceItem *)NULL);

   /* invalid role */
   QCOMPARE( m_model->data( m_model->index(0), 200), QVariant());
}

void test_SequenceModel::testSetData()
{
   bool result;

   SequenceItem *emptyItem = new SequenceItem();
   SequenceItem *newItem = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);

   m_model->addSequenceItem( emptyItem);
   QVariant newItemVariant = QVariant::fromValue<const SequenceItem *>(newItem);

   result = m_model->setData( m_model->index(0), newItemVariant, modelViewRules::Binary);
   QVERIFY( result == false);

   result = m_model->setData( m_model->index(0), newItemVariant, Qt::EditRole);
   QVERIFY( result == true);

   const SequenceItem *readItem;
   readItem = m_model->index(0).data(Qt::EditRole).value<const SequenceItem *>();
   QCOMPARE( *readItem, *newItem);
}

void test_SequenceModel::testSetDataInvalid()
{
   bool exceptionThrown = false;

   SequenceItem *newItem = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   QVariant newItemVariant = QVariant::fromValue<const SequenceItem *>(newItem);

   try
   {
      m_model->setData( QModelIndex(), newItemVariant, Qt::EditRole);
   }
   catch (QString & msg)
   {
      exceptionThrown = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   QVERIFY( exceptionThrown == true);
}

void test_SequenceModel::testSetDataInvalid2()
{
   bool exceptionThrown = false;

   SequenceItem *newItem = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   QVariant newItemVariant = QVariant::fromValue<const SequenceItem *>(newItem);

   try
   {
      m_model->setData( m_model->index(10), newItemVariant, Qt::EditRole);
   }
   catch (QString & msg)
   {
      exceptionThrown = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   QVERIFY( exceptionThrown == true);
}

void test_SequenceModel::testAddRows()
{
   SequenceItem *item1 = new SequenceItem();
   SequenceItem *item2 = new SequenceItem();
   SequenceItem *item3 = new SequenceItem();

   m_model->addSequenceItem( item1);
   m_model->addSequenceItem( item2);
   m_model->addSequenceItem( item3);

   QCOMPARE( m_model->rowCount(), 3);

   delete item1;
   delete item2;
   delete item3;
}

void test_SequenceModel::testAddNullItem()
{
   bool exceptionThrown = false;
   SequenceItem *item1 = NULL;

   m_model->addSequenceItem( item1);
   QCOMPARE( m_model->rowCount(), 1);

   /* retrieve null item */
   try
   {
      m_model->index(0).data(modelViewRules::Binary);
   }
   catch (QString & msg)
   {
      exceptionThrown = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   QVERIFY( exceptionThrown == true);
}

void test_SequenceModel::testRemoveRows()
{
   SequenceItem *item1 = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   SequenceItem *item2 = new SequenceItem( 100, QString("track_02.mp3"), SequenceItem::TRACK);
   SequenceItem *item3 = new SequenceItem( 150, QString("light_01"), SequenceItem::LIGHT);

   m_model->addSequenceItem( item1);
   m_model->addSequenceItem( item2);
   m_model->addSequenceItem( item3);

   /* remove one row */
   m_model->removeRow( 1);
   QCOMPARE( m_model->rowCount(), 2);

   const SequenceItem *secondItem;
   secondItem = m_model->index(1).data(modelViewRules::Binary).value<const SequenceItem *>();
   QCOMPARE( secondItem->label(), QString("light_01"));
   QCOMPARE( secondItem->delayMs(), 150);
   QCOMPARE( secondItem->indexOfNext(), SequenceItem::NEXT_IS_UNDEFINED);

   /* remove multiple rows */
   m_model->removeRows( 0, 2, QModelIndex());
   QCOMPARE( m_model->rowCount(), 0);

   delete item1;
   delete item2;
   delete item3;
}

void test_SequenceModel::testMoveRow()
{
   SequenceItem *item1 = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   SequenceItem *item2 = new SequenceItem( 100, QString("track_02.mp3"), SequenceItem::TRACK);

   m_model->addSequenceItem( item1);
   m_model->addSequenceItem( item2);

   const SequenceItem *readItem;
   readItem = m_model->index(0).data(modelViewRules::Binary).value<const SequenceItem *>();
   QCOMPARE( *readItem, *item1);

   /* move a row */
   bool res = m_model->moveRow( QModelIndex(), 0, QModelIndex(), 1);
   QVERIFY( res == true);

   readItem = m_model->index(0).data(modelViewRules::Binary).value<const SequenceItem *>();
   QCOMPARE( *readItem, *item2);

   delete item1;
   delete item2;
}

void test_SequenceModel::testInvalidMoveRow()
{
   bool res;
   SequenceItem *item1 = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   SequenceItem *item2 = new SequenceItem( 100, QString("track_02.mp3"), SequenceItem::TRACK);

   m_model->addSequenceItem( item1);
   m_model->addSequenceItem( item2);

   /* invalid source row */
   res = m_model->moveRows( QModelIndex(), -1, 1, QModelIndex(), 1);
   QVERIFY( res == false);

   res = m_model->moveRows( QModelIndex(), 10, 1, QModelIndex(), 1);
   QVERIFY( res == false);


   /* invalid count */
   res = m_model->moveRows( QModelIndex(), 0, -1, QModelIndex(), 1);
   QVERIFY( res == false);

   res = m_model->moveRows( QModelIndex(), 0, 10, QModelIndex(), 1);
   QVERIFY( res == false);

   /* invalid destination */
   res = m_model->moveRows( QModelIndex(), 0, 1, QModelIndex(), -1);
   QVERIFY( res == false);
   res = m_model->moveRows( QModelIndex(), 0, 1, QModelIndex(), 10);
   QVERIFY( res == false);

   /* prove that items are still the same */
   const SequenceItem *readItem1, *readItem2;
   readItem1 = m_model->index(0).data(modelViewRules::Binary).value<const SequenceItem *>();
   readItem2 = m_model->index(1).data(modelViewRules::Binary).value<const SequenceItem *>();
   QCOMPARE( *readItem1, *item1);
   QCOMPARE( *readItem2, *item2);

   delete item1;
   delete item2;
}

void test_SequenceModel::testRemoveNothing()
{
   SequenceItem *item1 = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   SequenceItem *item2 = new SequenceItem( 100, QString("track_02.mp3"), SequenceItem::TRACK);

   m_model->addSequenceItem( item1);
   m_model->addSequenceItem( item2);

   /* remove zero elements */
   QCOMPARE(m_model->removeRows( 0, 0, QModelIndex()), true);

   delete item1;
   delete item2;
}

void test_SequenceModel::testInvalidRemoveRow()
{
   bool exceptionLow = false, exceptionHi = false;

   SequenceItem *item1 = new SequenceItem( 50, QString("track_01.mp3"), SequenceItem::TRACK);
   m_model->addSequenceItem( item1);

   try
   {
      /* invalid row */
      m_model->removeRows( -1, 1, QModelIndex());
   }
   catch (QString & msg)
   {
      exceptionLow = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   try
   {
      /* invalid count */
      m_model->removeRows( 0, 10, QModelIndex());
   }
   catch (QString & msg)
   {
      exceptionHi = true;
      QVERIFY( msg.contains("Assert fail:"));
   }

   QVERIFY( exceptionLow == true);
   QVERIFY( exceptionHi == true);

   delete item1;
}

